#include "cpu/helper.h"
#include "cpu/decode/modrm.h"

#include "all-instr.h"

typedef int (*helper_fun)(swaddr_t);
static make_helper(_2byte_esc);

#define make_group(name, item0, item1, item2, item3, item4, item5, item6, item7)       \
    static helper_fun concat(opcode_table_, name)[8] = {                               \
        /* 0x00 */ item0, item1, item2, item3, /* 0x04 */ item4, item5, item6, item7}; \
    static make_helper(name) {                                                         \
        ModR_M m;                                                                      \
        m.val = instr_fetch(eip + 1, 1);                                               \
        return concat(opcode_table_, name)[m.opcode](eip);                             \
    }

/* 0x80 */
make_group(group1_b, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0x81 */
    make_group(group1_v, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0x83 */
    make_group(group1_sx_v, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xc0 */
    make_group(group2_i_b, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xc1 */
    make_group(group2_i_v, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xd0 */
    make_group(group2_1_b, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xd1 */
    make_group(group2_1_v, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xd2 */
    make_group(group2_cl_b, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xd3 */
    make_group(group2_cl_v, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xf6 */
    make_group(group3_b, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xf7 */
    make_group(group3_v, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xfe */
    make_group(group4, inv, inv, inv, inv, inv, inv, inv, inv)

    /* 0xff */
    make_group(group5, inv, inv, inv, inv, inv, inv, inv, inv)

        make_group(group6, inv, inv, inv, inv, inv, inv, inv, inv)

            make_group(group7, inv, inv, inv, inv, inv, inv, inv, inv)


    /* TODO: Add more instructions!!! */
    // typedef int (*helper_fun)(swaddr_t);
    helper_fun opcode_table[256] = {
        /* 0x00 */ inv,           inv,           inv,           inv,
        /* 0x04 */ inv,           inv,           inv,           inv,
        /* 0x08 */ inv,           inv,           inv,           inv,
        /* 0x0c */ inv,           inv,           inv,           _2byte_esc,
        /* 0x10 */ inv,           inv,           inv,           inv,
        /* 0x14 */ inv,           inv,           inv,           inv,
        /* 0x18 */ inv,           inv,           inv,           inv,
        /* 0x1c */ inv,           inv,           inv,           inv,
        /* 0x20 */ inv,           inv,           inv,           inv,
        /* 0x24 */ inv,           inv,           inv,           inv,
        /* 0x28 */ inv,           inv,           inv,           inv,
        /* 0x2c */ inv,           inv,           inv,           inv,
        /* 0x30 */ inv,           inv,           inv,           inv,
        /* 0x34 */ inv,           inv,           inv,           inv,
        /* 0x38 */ inv,           inv,           inv,           inv,
        /* 0x3c */ inv,           inv,           inv,           inv,
        /* 0x40 */ inv,           inv,           inv,           inv,
        /* 0x44 */ inv,           inv,           inv,           inv,
        /* 0x48 */ inv,           inv,           inv,           inv,
        /* 0x4c */ inv,           inv,           inv,           inv,
        /* 0x50 */ inv,           inv,           inv,           inv,
        /* 0x54 */ inv,           inv,           inv,           inv,
        /* 0x58 */ inv,           inv,           inv,           inv,
        /* 0x5c */ inv,           inv,           inv,           inv,
        /* 0x60 */ inv,           inv,           inv,           inv,
        /* 0x64 */ inv,           inv,           operand_size,  inv,
        /* 0x68 */ inv,           inv,           inv,           inv,
        /* 0x6c */ inv,           inv,           inv,           inv,
        /* 0x70 */ inv,           inv,           inv,           inv,
        /* 0x74 */ inv,           inv,           inv,           inv,
        /* 0x78 */ inv,           inv,           inv,           inv,
        /* 0x7c */ inv,           inv,           inv,           inv,
        /* 0x80 */ group1_b,      group1_v,      inv,           group1_sx_v,
        /* 0x84 */ inv,           inv,           inv,           inv,
        /* 0x88 */ mov_r2rm_b,    mov_r2rm_v,    mov_rm2r_b,    mov_rm2r_v,
        /* 0x8c */ inv,           inv,           inv,           inv,
        /* 0x90 */ inv,           inv,           inv,           inv,
        /* 0x94 */ inv,           inv,           inv,           inv,
        /* 0x98 */ inv,           inv,           inv,           inv,
        /* 0x9c */ inv,           inv,           inv,           inv,
        /* 0xa0 */ mov_moffs2a_b, mov_moffs2a_v, mov_a2moffs_b, mov_a2moffs_v,
        /* 0xa4 */ inv,           inv,           inv,           inv,
        /* 0xa8 */ inv,           inv,           inv,           inv,
        /* 0xac */ inv,           inv,           inv,           inv,
        /* 0xb0 */ mov_i2r_b,     mov_i2r_b,     mov_i2r_b,     mov_i2r_b,
        /* 0xb4 */ mov_i2r_b,     mov_i2r_b,     mov_i2r_b,     mov_i2r_b,
        /* 0xb8 */ mov_i2r_v,     mov_i2r_v,     mov_i2r_v,     mov_i2r_v,
        /* 0xbc */ mov_i2r_v,     mov_i2r_v,     mov_i2r_v,     mov_i2r_v,
        /* 0xc0 */ group2_i_b,    group2_i_v,    inv,           inv,
        /* 0xc4 */ inv,           inv,           mov_i2rm_b,    mov_i2rm_v,
        /* 0xc8 */ inv,           inv,           inv,           inv,
        /* 0xcc */ int3,          inv,           inv,           inv,
        /* 0xd0 */ group2_1_b,    group2_1_v,    group2_cl_b,   group2_cl_v,
        /* 0xd4 */ inv,           inv,           nemu_trap,     inv,
        /* 0xd8 */ inv,           inv,           inv,           inv,
        /* 0xdc */ inv,           inv,           inv,           inv,
        /* 0xe0 */ inv,           inv,           inv,           inv,
        /* 0xe4 */ inv,           inv,           inv,           inv,
        /* 0xe8 */ inv,           inv,           inv,           inv,
        /* 0xec */ inv,           inv,           inv,           inv,
        /* 0xf0 */ inv,           inv,           inv,           inv,
        /* 0xf4 */ inv,           inv,           group3_b,      group3_v,
        /* 0xf8 */ inv,           inv,           inv,           inv,
        /* 0xfc */ inv,           inv,           group4,        group5};

helper_fun _2byte_opcode_table[256] = {
    /* 0x00 */ group6, group7, inv, inv,
    /* 0x04 */ inv,    inv,    inv, inv,
    /* 0x08 */ inv,    inv,    inv, inv,
    /* 0x0c */ inv,    inv,    inv, inv,
    /* 0x10 */ inv,    inv,    inv, inv,
    /* 0x14 */ inv,    inv,    inv, inv,
    /* 0x18 */ inv,    inv,    inv, inv,
    /* 0x1c */ inv,    inv,    inv, inv,
    /* 0x20 */ inv,    inv,    inv, inv,
    /* 0x24 */ inv,    inv,    inv, inv,
    /* 0x28 */ inv,    inv,    inv, inv,
    /* 0x2c */ inv,    inv,    inv, inv,
    /* 0x30 */ inv,    inv,    inv, inv,
    /* 0x34 */ inv,    inv,    inv, inv,
    /* 0x38 */ inv,    inv,    inv, inv,
    /* 0x3c */ inv,    inv,    inv, inv,
    /* 0x40 */ inv,    inv,    inv, inv,
    /* 0x44 */ inv,    inv,    inv, inv,
    /* 0x48 */ inv,    inv,    inv, inv,
    /* 0x4c */ inv,    inv,    inv, inv,
    /* 0x50 */ inv,    inv,    inv, inv,
    /* 0x54 */ inv,    inv,    inv, inv,
    /* 0x58 */ inv,    inv,    inv, inv,
    /* 0x5c */ inv,    inv,    inv, inv,
    /* 0x60 */ inv,    inv,    inv, inv,
    /* 0x64 */ inv,    inv,    inv, inv,
    /* 0x68 */ inv,    inv,    inv, inv,
    /* 0x6c */ inv,    inv,    inv, inv,
    /* 0x70 */ inv,    inv,    inv, inv,
    /* 0x74 */ inv,    inv,    inv, inv,
    /* 0x78 */ inv,    inv,    inv, inv,
    /* 0x7c */ inv,    inv,    inv, inv,
    /* 0x80 */ inv,    inv,    inv, inv,
    /* 0x84 */ inv,    inv,    inv, inv,
    /* 0x88 */ inv,    inv,    inv, inv,
    /* 0x8c */ inv,    inv,    inv, inv,
    /* 0x90 */ inv,    inv,    inv, inv,
    /* 0x94 */ inv,    inv,    inv, inv,
    /* 0x98 */ inv,    inv,    inv, inv,
    /* 0x9c */ inv,    inv,    inv, inv,
    /* 0xa0 */ inv,    inv,    inv, inv,
    /* 0xa4 */ inv,    inv,    inv, inv,
    /* 0xa8 */ inv,    inv,    inv, inv,
    /* 0xac */ inv,    inv,    inv, inv,
    /* 0xb0 */ inv,    inv,    inv, inv,
    /* 0xb4 */ inv,    inv,    inv, inv,
    /* 0xb8 */ inv,    inv,    inv, inv,
    /* 0xbc */ inv,    inv,    inv, inv,
    /* 0xc0 */ inv,    inv,    inv, inv,
    /* 0xc4 */ inv,    inv,    inv, inv,
    /* 0xc8 */ inv,    inv,    inv, inv,
    /* 0xcc */ inv,    inv,    inv, inv,
    /* 0xd0 */ inv,    inv,    inv, inv,
    /* 0xd4 */ inv,    inv,    inv, inv,
    /* 0xd8 */ inv,    inv,    inv, inv,
    /* 0xdc */ inv,    inv,    inv, inv,
    /* 0xe0 */ inv,    inv,    inv, inv,
    /* 0xe4 */ inv,    inv,    inv, inv,
    /* 0xe8 */ inv,    inv,    inv, inv,
    /* 0xec */ inv,    inv,    inv, inv,
    /* 0xf0 */ inv,    inv,    inv, inv,
    /* 0xf4 */ inv,    inv,    inv, inv,
    /* 0xf8 */ inv,    inv,    inv, inv,
    /* 0xfc */ inv,    inv,    inv, inv};


/*
    int exec(swaddr_t eip)
*/
make_helper(exec) {
    ops_decoded.opcode = instr_fetch(eip, 1);
    return opcode_table[ops_decoded.opcode](eip);
}

static make_helper(_2byte_esc) {
    eip++;
    uint32_t opcode = instr_fetch(eip, 1);
    ops_decoded.opcode = opcode | 0x100;
    return _2byte_opcode_table[opcode](eip) + 1;
}
